/*!
* @description: 较为常用的工具集合
* @author: kelichao
* @update: 2016-11-19
* @update: 2016-12-28 / 增加了埋点文件以及调用客户端接口文件的方法
* @update: 2017-01-04 / 增加了MVC架构
* @update: 2017-02-25 / 开始增加移动端函数
* @update: 2020-09-01 / 增加Fpi的函数
* @https://github.com/Kelichao/klcs.js
*/

// (function (global, factory) {
//   typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
//   typeof define === 'function' && define.amd ? define('underscore', factory) :
//   (global = typeof globalThis !== 'undefined' ? globalThis : global || self, (function () {
//     var current = global._;
//     var exports = global._ = factory();
//     exports.noConflict = function () { global._ = current; return exports; };
//   }()));
// }(this, (function () {

// })));
// 防止undefined被改写
(function (undefined) {
// root 的值, 客户端为window, 服务端(node) 中为 exports
var root = this || window || {};
var host = document.location.host;

// 判断对象类型
var kind = Object.prototype.toString;

// 方法简写
// 缓存变量, 便于压缩代码
// 此处「压缩」指的是压缩到 min.js 版本
// 而不是 gzip 压缩
// 同时可减少在原型链中的查找次数(提高代码效率
var ArrayProto = Array.prototype,
ObjProto = Object.prototype,
FuncProto = Function.prototype;

var push = ArrayProto.push,
pop = ArrayProto.pop,
href = window.location.href,
slice = ArrayProto.slice;

// 安全构造函数法
var klcs = function (obj) {
// 如果传入了参数，且参数是A的实例对象，直接返回该参数对象
// 否则就把obj当参数使用了
if (obj instanceof klcs) {
  return obj;
}

// 如果忘记了实例化，this的prototype就没有保存着A的prototype地址
if (!(this instanceof klcs)) {
  return new klcs(obj);
}

// 如果obj并不是klcs的实例
// 且这里已经处于正常实例化阶段
// 用于OOP方式调用，将传入的对象赋值给内部对象
// wrapped仅仅用来保存对象
this._wrapped = obj;

/*
  * 在new状态下，return返回的都是当前的this ->
  * 且this指向的地方都是新开辟的堆空间地址
  * 默认就是这种模式，可以不写
  */
// return this;
};

//原型对象挂载简写
var fn = (klcs.fn = klcs.prototype);

// 兼容node模块
// node中有exports/module.exports模块用于导出某个js文件
// node中可以用CMD模式调用此文件：var MATH = require("./MATH")
// 直接检验exports 是兼顾老版本nodeAPI
// if (typeof exports !== "undefined") {

//     // 在正常node环境中
//     // module以及module.exports都存在
//     if (typeof module !== "undefined" && module.exports) {

//         // exports 原本挂载的是module.exports对象地址，
//         // 如果重写module.exports为klcs对象则需要顺带
//         // 将exports地址指向klcs，重写后他们两者会丢失联系
//         exports = module.exports = klcs;
//     }

//     exports.klcs = klcs;

// } else {
//     // 把整体绑定在全局变量
//     // 其实就是global.klcs
//     root.klcs = klcs;
// }

// defined这个方法是用于判断变量是否被定义
// 如果还没有定义 Uncaught ReferenceError: * is not defined(…) -> 0
// 未定义的变量，建议直接typeof
klcs.isUndefined = function (variable) {
if (typeof variable === "undefined") {
  return true;
}
return false;
};

// 类型映射,目前是11种
var typeMap = {
isObject: "[object Object]",
isArray: "[object Array]",
isBoolean: "[object Boolean]",
isFunction: "[object Function]",
isString: "[object String]",
isMath: "[object Math]",
isDate: "[object Date]",
isNull: "[object Null]",
isRegExp: "[object RegExp]",
isNumber: "[object Number]",
isError: "[object Error]",
isWindow: "[object Window]", // win对象比较特别
};

// 内部生成对象判断函数
var _creatrTypeFunction = function (object) {
// 这里没有加var导致在ie中报错
for (var item in object) {
  // 将方法挂载到对象上,每个item一个闭包空间来去除闭包影响
  // underscore用forEach生产，原理跟这个一样,后期可以调整
  klcs[item] = (function (val) {
    return function (total) {
      return kind.call(total) === object[val];
    };
  })(item);
}
};

_creatrTypeFunction(typeMap);

// forEach负责用来遍历对象/数组属性
klcs.each =
klcs.forEach =
klcs.foreach =
  function (total, fn) {
    var isObj = klcs.isObject(total);
    var isFun = klcs.isFunction(total);
    var isWin = klcs.isWindow(total);
    var isArr = klcs.isArray(total);

    if (isObj || isFun || isWin) {
      for (var i in total) {
        fn(total[i], i, total);
      }
    } else if (isArr) {
      var j = 0;
      for (; j < total.length; j++) {
        fn(total[j], j, total);
      }
    }
  };

// 判断对象或者数组是否为空
klcs.isEmpty = function (total) {
var flag = true;
if (klcs.isObject(total)) {
  klcs.some(total, function () {
    flag = false;
    return false;
  });
} else if (klcs.isArray(total)) {
  if (total.length > 0) {
    flag = false;
  }
}

return flag;
};

// some如果有一项返回false则返回false
klcs.some = function (total, fn) {
var flag = "";
if (klcs.isObject(total)) {
  for (var i in total) {
    flag = fn(total[i], i);
    if (flag === false) {
      return false;
    }
  }
} else if (klcs.isArray(total)) {
  for (var j = 0; j < total.length; j++) {
    flag = fn(total[j], j);
    if (flag === false) {
      return false;
    }
  }
}

// 没有返回false的项目
return true;
};

// 拆分一个字符串中间有间隔的字符串
// 如："aaa    bbb cc    ddd"
klcs.splitSpace = function (str) {
return str.split(/\s+/g);
};

// 去除字符串两边的空格
// 如果有第二个参数，则把所有空格删除
// klcs.trim("  dsfdsa=- 234.;df  ");
klcs.trim = function (str, state) {
var totalStr = "";

if (state === true) {
  totalStr = str.replace(/\s/g, "");
  return totalStr;
}

if (klcs.isString(str) === true) {
  // 成功klcs.trim("  fsdf f     ");
  totalStr = str.replace(/((^\s*)|(\s*$))/g, "");
} else {
  throw Error("需要解析的并不是字符串");
}

// 失败：如果末尾有两个以上的空格就读取失败了
// str.replace(/^\s+((\S|\s)*)\s+$/g, "$1");
// jq写法   str.replace(/^\s+|((?:^|[^\\\\])(?:\\\\.)*)\s+$/g, "$1");
return totalStr;
};

// 拆分规律字符串函数
// key键值， string输入的串，type分割类型，flag是否除去首个问号字符
var _strToObject = function (key, string, type, flag) {
var str = string,
  arr = [],
  obj = {},
  first = null,
  final = null,
  cont = "";

if (typeof str === "string" && str.length !== 0) {
  // 注意，window.location.search 截取的串都是带问号的
  // 如果有问号则去除问号
  if (flag) {
    str = str.search(/^\?/g) !== -1 ? str.substring(1) : str;
  }

  arr = str.split(type);
  klcs.forEach(arr, function (value) {
    cont = value.split("=");
    first = klcs.trim(cont[0]);
    final = klcs.trim(cont[1]);
    obj[first] = final;
  });
}

if (!!key) {
  obj = obj[key];
}

return obj;
};

/**
 * 将参数字符串转化成JSON对象。
 * @param {*} key 为需要取得的键值
 * @param {*} address 地址串，不填则为location.search
 * exp: klcs.locaSearch("fsd","?sfsd=3423&we=234&fsd=324");  =   324
 *      klcs.locaSearch("","?sfsd=3423&we=234&fsd=324");     =  {sfsd:3423,we:234,fsd:324}
 */
klcs.locaSearch = function (key, address) {
address = address || location.search;
var total = _strToObject(key, address, "&", true);

return total;
};

klcs.getWeek = function () {
var datas = dayjs().day();
var week = ["日", "一", "二", "三", "四", "五", "六"];
return week[datas];
};

/*
* 获取路由的参数，比较奇特
* 2020.9.1添加
*/
klcs.getQueryString = function (name) {
var href = decodeURI(window.location.href);
var v1 = [];
var v2 = [];
v1 = href.split(name + "=");
if (v1.length > 1) {
  v2 = v1[1].split("&");
  if (v2.length > 1) {
    return v2[0];
  } else {
    return v1[1];
  }
} else {
  return "";
}
};
//     function getUrlParam(name) {
//  var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); //构造一个含有目标参数的正则表达式对象
//  var r = window.location.search.substr(1).match(reg); //匹配目标参数
//  if (r != null) return unescape(r[2]); return null; //返回参数值
// }

/**
 * 把cookie字符串转化为对象获取函数
 * @param {*} key
 * @param {*} string
 * aaa=123;bbb=456;ccc=678
 * =  {
 *   a:123,
 *   bbb:456,
 *   ccc=678
 * }
 */
klcs.cookie = function (key, string) {
var cookie = string || window.document.cookie,
  total = "";

// 如果传入的字符串结尾带了分号(;)，则进行删除
if (cookie.slice(-1) === ";") {
  cookie = cookie.slice(0, -1);
}

total = _strToObject(key, cookie, ";", false);
// 测试用例klcs.cookie("bbb","aaa=123;bbb=789");
return total;
};

/**
 * description：用递归方法拷贝深层次对象,得到全新对象
 * @param {*} context 
 * @param {*} res 
 * 
 * // 对象要求：属性中有对象，且该内部对象有简单类型。
  var person = {
    name: "Bob",
    sing:{
      "name":"发如雪"
    }
  };
  var x =klcs.mixin(person);
  person.sing.name = "";
  console.log(x);
  */
var _recursive = function (context, res) {
//这里增加了数组处理，暂时还不清楚函数体如何进行复制。
var arg = arguments.callee,
  object = context instanceof Array ? [] : {};

// 继承原先的对象键值对
klcs.forEach(res, function (value, key) {
  object[key] = res[key];
});

// 遍历中间值的键值对
klcs.forEach(context, function (value, key) {
  // 如果键值对的属性是一个引用对象
  if (klcs.isObject(context[key]) || klcs.isArray(context[key])) {
    object[key] = arg(context[key], res[key]);

    // 如果是一个简单对象，则直接赋值
  } else {
    object[key] = context[key];
  }
});
return object;
};

// 糅合属性方法
klcs.mixin = function () {
var total,
  flag,
  result,
  i = 0,
  temporary = {},
  length = arguments.length;

// 如果第一个是状态码，把total换到arg[1];
if (typeof arguments[i] === "boolean") {
  flag = arguments[i];
  i++;
}

// 这里增加了数组处理，暂时还不清楚函数体如何进行复制。
// total = arguments[i];
result = arguments[i];

// i 当前参数下标
// length参数长度
for (; i < length; i++) {
  // 当前被拷贝的对象
  total = arguments[i];

  // 深度克隆
  if (flag === true) {
    temporary = _recursive(total, result);

    // 得到temporary后进行浅复制
    klcs.forEach(temporary, function (value, key) {
      result[key] = value;
    });

    // 浅复制
  } else {
    klcs.forEach(total, function (value, key) {
      result[key] = value;
    });
  }
}
return result;
};

klcs.query = function (id) {
return document.querySelector(id);
};

klcs.querys = function (class1) {
return document.querySelectorAll(class1);
};

/**
 * 1.对象键值对的改写，赋值
 * data = {
    object: {
      title:123,
      title1:456
    },
    in: ["title","title1"],
    out: ["name","name1"]
  }
  =》 {title: 123, title1: 456, name: 123, name1: 456}
  
  2.数组模式
  data = {
    context: [{
      title:123,
      title1:456
    },{
      title:777,
      title1:456 
    }],
    in: ["title","title1"],
    out: ["name","name1"]
  }
  =》
  [{title: 123, title1: 456, name: 123, name1: 456},
  {title: 777, title1: 456, name: 777, name1: 456}]
  * @param {*} data 
  */
function changeKeyNames(data) {
if (!data.context.length) {
  data.in.forEach(function (value, key) {
    data.context[data.out[key]] = data.context[value];
  });
} else {
  data.context.forEach(function (item) {
    data.in.forEach(function (value, key) {
      item[data.out[key]] = item[value];
    });
  });
}
return data.context;
}

// 判断是否在小程序端
klcs.inWxWebView = function () {
var flag = !!(
  (navigator.userAgent.match(/micromessenger/i) &&
    navigator.userAgent.match(/miniprogram/i)) ||
  window.__wxjs_environment === "miniprogram"
);

return flag;
};

/**
 * 判断手机平台
 * iphone(ipad)
 * gphone
 * wechat
 * @这个准备停止维护
 * @后续使用klcs.platformType函数
 */
klcs.mobileType = function () {
var type;
//获取语言
var language = (
  navigator.browserLanguage || navigator.language
).toLowerCase();
// 区分函数
var _dist = function () {
  // 获取版本号主要字段
  var methods = navigator.userAgent;

  // navigator.appVersion
  return {
    iPhone: methods.indexOf("iPhone") != -1 || methods.indexOf("Mac") != -1,
    iPad: methods.indexOf("iPad") != -1,
    wechat: klcs.inWxWebView(),
  };
};

if (_dist().wechat) {
  type = "wechat";
} else if (_dist().iPhone || _dist().iPad) {
  type = "iphone";
} else {
  type = "gphone";
}

return type;
};

/* 
    当前页面 URL： http://localhost:9001/#/erp/workSpace
    当前页面主机名和端口号： localhost:9001
    当前页面主机名： localhost
    当前页面协议： http:
    */
klcs.getIp = function () {
return {
  "当前页面 URL：": window.location.href,
  "当前页面主机名和端口号：": window.location.host,
  "当前页面主机名：": window.location.hostname,
  "当前页面协议：": window.location.protocol,
};
};

// klcs.isTest = function() {
//   return location.href.match("localhost");
// }

// tab切换逻辑块
klcs.tabToggle = function (elements, _class, fn, state) {
$(document).on("click", elements, function () {
  var $this = $(this);
  var index = $(this).index();
  var data = $this.attr("tab-data");
  var flag = $this.hasClass(_class);

  if (state === true && flag === true) {
    return;
  }
  $(elements).removeClass(_class);
  $this.addClass(_class);
  fn(data, index);
});
};

// 客户端用户id
var CLIENT_USERID;
klcs.CLIENT_USERID = CLIENT_USERID = klcs.cookie("userid");

// 客户端版本号
klcs.CLIENT_VERSION = klcs.cookie("version")
? klcs.cookie("version").split(".").slice(-1)[0]
: "";

// 单例模式，once的改进版
klcs.getSingle = function (fn) {
var result; // 为了存储变量
return function () {
  // 改变this指向
  // 通过arguments可以将未知的参数按照原样传入
  return result || (result = fn.apply(this, arguments));
};
};

// 参数解释一下
// str: 需要加密的字符串
// key1: 加密的key
// mode: 加密模式
// padding: 填充模式
// klcs.Encrypt("aaaaa", "za2n3fKNXj5JA8BR","ECB","Pkcs7")
klcs.Encrypt = function (str, key1, mode, padding) {
// let key = CryptoJS.enc.Utf8.parse(fillKey(key1));
let key = CryptoJS.enc.Utf8.parse(key1);
var encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(str), key, {
  // iv:  CryptoJS.enc.Utf8.parse('0123456'),// 偏移量
  mode: CryptoJS.mode[mode], // 加密模式
  padding: CryptoJS.pad[padding], // 填充模式
});

// 需要返回base64格式的加密结果，使用此句
// return CryptoJS.enc.Base64.stringify(encrypted.ciphertext);

// 需要返回hex格式的加密结果，使用此句
return encrypted.ciphertext.toString().toUpperCase();
};

//解密方法
klcs.Decrypt = function (str, key1, mode, padding) {
const key = CryptoJS.enc.Utf8.parse(key1); //十六位十六进制数作为密钥

// 这两行代码没有,导致输出位空
let encryptedHexStr = CryptoJS.enc.Hex.parse(str);
let srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr);

let decrypt = CryptoJS.AES.decrypt(srcs, key, {
  mode: CryptoJS.mode[mode],
  padding: CryptoJS.pad[padding],
});
return decrypt.toString(CryptoJS.enc.Utf8);
};

// 客户端匿名机制函数
// 用户名加星号
klcs.encryptName = function (nameparam) {
var name = nameparam.toString();
var length = name.length;
var retName = "";
switch (length) {
  case 1:
    retName = name;
    break;
  case 2:
    var nameArr = name.split("");
    retName = nameArr[0] + "*";
    break;
  case 3:
    var nameArr = name.split("");
    retName = nameArr[0] + "*" + nameArr[length - 1];
    break;
  case 4:
    var nameArr = name.split("");
    retName = nameArr[0] + "**" + nameArr[length - 1];
    break;
  case 5:
    var nameArr = name.split("");
    retName = nameArr[0] + "***" + nameArr[length - 1];
    break;
  default:
    var nameArr = name.split("");
    retName = name.substr(0, length - 4) + "***" + nameArr[length - 1];
    break;
}

return retName;
};

// 动态创建dom元素
klcs.addScript = function (url, callback) {
// 创建dom元素
var script = document.createElement("script");

// 设置属性
script.type = "text/javascript";
script.src = url;

// 此处参考seaJS中加载代码时所用的
// ie8中这个就是false ，chrome中为true
var supportOnload = "onload" in script;

if (supportOnload) {
  script.onload = function () {
    // success code
    callback();
  };
} else {
  script.onreadystatechange = function () {
    // IE8中这两个属性值会有两个阶段  1.loading   2.loaded 某些情况下是complete
    if (
      script.readyState === "loaded" ||
      script.readyState === "complete"
    ) {
      // success code
      callback();
    }
  };
}

document.body.appendChild(script);
};

klcs.splitParamsArray = function (params) {
var obj = {};
klcs.foreach(params, (value, key) => {
  if (klcs.isArray(value)) {
    obj[key] = value.join(",");
  } else {
    obj[key] = value;
  }
});
return obj;
};

/**
数字三位数分割
*/
klcs.moneyFilter = function (val) {
if (val === undefined) return "";
val = val.toFixed(fix); // 保留小数2位
val = "" + val; // 转换成字符串
let fuhao = ""; //判断是否负数
if (val[0] == "-") {
  fuhao = "-";
  val = val.substring(1, val.length);
}
var int = fix !== 0 ? val.slice(0, fix * -1 - 1) : val; // 拿到整数
var ext = fix !== 0 ? val.slice(fix * -1 - 1) : ""; // 获取到小数
//每个三位价格逗号
int = int.split("").reverse().join(""); // 翻转整数
var temp = ""; // 临时变量
for (var i = 0; i < int.length; i++) {
  temp += int[i];
  if ((i + 1) % 3 == 0 && i != int.length - 1) {
    temp += ","; // 每隔三个数字拼接一个逗号
  }
}
temp = temp.split("").reverse().join(""); // 加完逗号之后翻转
temp = temp + ext; // 整数小数拼接
return fuhao + temp; // 返回
};

klcs.isNumberOrString = function(value) {
  return (
    // 是数字类型且非 NaN
    (typeof value === 'number' && !isNaN(value)) ||
    // 是字符串且可转换为有效数字
    (typeof value === 'string' && 
     !isNaN(parseFloat(value)) && 
     isFinite(value))
  );
}
  
klcs.splitArray = function (array = [], size) {
return array.join(",");
};

  // 深度拷贝,抄的jquery的,用法也跟jquery一样!!!!!!
  // klcs.deepClone(true, [], data1)
  klcs.deepClone = function () {
    var options,
      name,
      src,
      copy,
      copyIsArray,
      clone,
      target = arguments[0] || {},
      i = 1,
      length = arguments.length,
      deep = false;

    // Handle a deep copy situation
    if (typeof target === "boolean") {
      deep = target;

      // Skip the boolean and the target
      target = arguments[i] || {};
      i++;
    }

    // Handle case when target is a string or something (possible in deep copy)
    if (typeof target !== "object" && !klcs.isFunction(target)) {
      target = {};
    }

    // Extend klcs itself if only one argument is passed
    if (i === length) {
      target = this;
      i--;
    }

    for (; i < length; i++) {
      // Only deal with non-null/undefined values
      if ((options = arguments[i]) != null) {
        // Extend the base object
        for (name in options) {
          copy = options[name];

          // Prevent Object.prototype pollution
          // Prevent never-ending loop
          if (name === "__proto__" || target === copy) {
            continue;
          }

          // Recurse if we're merging plain objects or arrays
          if (
            deep &&
            copy &&
            (klcs.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))
          ) {
            src = target[name];

            // Ensure proper type for the source value
            if (copyIsArray && !Array.isArray(src)) {
              clone = [];
            } else if (!copyIsArray && !klcs.isPlainObject(src)) {
              clone = {};
            } else {
              clone = src;
            }
            copyIsArray = false;

            // Never move original objects, clone them
            target[name] = klcs.deepClone(deep, clone, copy);

            // Don't bring in undefined values
          } else if (copy !== undefined) {
            target[name] = copy;
          }
        }
      }
    }

    // Return the modified object
    return target;
  };
  var getProto = Object.getPrototypeOf;
  var class2type = {};

  var toString = class2type.toString;

  var hasOwn = class2type.hasOwnProperty;

  var fnToString = hasOwn.toString;

  var ObjectFunctionString = fnToString.call(Object);

  klcs.isPlainObject = function (obj) {
    var proto, Ctor;

    // Detect obvious negatives
    // Use toString instead of jQuery.type to catch host objects
    if (!obj || toString.call(obj) !== "[object Object]") {
      return false;
    }

    proto = getProto(obj);

    // Objects with no prototype (e.g., `Object.create( null )`) are plain
    if (!proto) {
      return true;
    }

    // Objects with prototype are plain iff they were constructed by a global Object function
    Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
    return (
      typeof Ctor === "function" &&
      fnToString.call(Ctor) === ObjectFunctionString
    );
  };

// 写一个深拷贝方法
klcs.clone = function (obj) {
var result = Array.isArray(obj) ? [] : {};
for (var key in obj) {
  if (obj.hasOwnProperty(key)) {
    if (typeof obj[key] === "object" && obj[key] !== null) {
      result[key] = klcs.deepCopy(obj[key]); // 递归复制
    } else {
      result[key] = obj[key];
    }
  }
}
return result;
};

// 指定bind对象，原生bind
klcs.bind = function (context, fn) {
var final = null;
// 闭包保留内存context与fn
if (klcs.isFunction(fn)) {
  final = function () {
    return fn.apply(context, arguments);
  };
} else {
  // 如果不是回调函数，直接返回fn
  final = fn;
}

return final;
};

// 数据模型，比如总参数，总配置，分段参数等
// 如new一个
// opts = new klcs.Model({a:1,b:2});
klcs.Model = function (total) {
if (this instanceof klcs.Model) {
  this._options = total;
  // this._changeFunction = changeFunction;
} else {
  return new klcs.Model(total);
}
};

// 模型的原型链方法
klcs.Model.prototype = {
constructor: klcs.Model,
//有些情况下直接取值
// 最原始的取值法
self: function () {
  return this._options;
},
// 只有数据模型才具有的set,get方法
get: function (key) {
  return this._options[key];
},
// 设置模型的值
set: function (value) {
  var temporary = this._isChanged(this._options, value),
    final = klcs.mixin(true, this._options, value);

  if (temporary === false) {
    this.change(final);
  }
  return final;
},
// 触发变动
change: function (final) {
  // console.log("mode发生了变动");
  // console.log(final);
},
// 判断参数(obj/arr)覆盖原值后原值是否改变
_isChanged: function (total, add) {
  return klcs(add).some(function (value, key) {
    return value == total[key];
  });
},
};

// 控制器
// 将this强制绑定到控制器本身，用于this调用
// 暂时只用于单纯的放下业务逻辑
klcs.Controller = function (fn) {
if (this instanceof klcs.Controller) {
  // 控制器遍历将this指向变更
  var controlEach = klcs.bind(this, function (value, key) {
    // 将绑定到dom的this指向改变
    this[key] = klcs.bind(this, value);

    // 不改变dom的this指向
    // this[key] = value;
  });

  klcs.forEach(fn, controlEach);
  // this.fn = fn;
} else {
  return new klcs.Controller(fn);
}
};

klcs.Controller.prototype = {
constructor: klcs.Controller,
// 回调队列
// 回调列队使用方法
subscribe: function (parameter) {
  var callback = parameter.callback,
    queue = parameter.queue,
    callbackArray = [],
    bindFn = klcs.bind(this, function (value) {
      if (klcs.isFunction(this[value])) {
        callbackArray.push(this[value]);
      } else {
        this.myLog(value);
      }
    });

  // 集成callback
  klcs.forEach(callback, bindFn);

  // 触发列队
  this.deliver(callbackArray, queue);
},
myLog: function (value) {
  console.info("callback:" + value + " is not a function");
},
// 触发列队
deliver: function (callArr, queue) {
  var bindFn = klcs.bind(this, function (value, key) {
    if (klcs.isFunction(this[key])) {
      this[key].apply(null, value);
    } else {
      this.myLog(key);
    }
  });

  // 触发普通列队
  klcs.forEach(callArr, function (fn) {
    fn();
  });

  // 触发带参数的列队
  // 集成queueArray
  klcs.forEach(queue, bindFn);
},
};

// 通过视图绑定页面事件,是Model与Controller的载体
// klcs.View({
// 	current: document,
//  control: control,
// 	events:{
// 		"div": "click fn1", // 当心"aa bb "这种情况trim去除
// 		".bbb": "change fn2",
//		"#ccc": "fn3"
// 	}
// });
klcs.View = function (total) {
if (this instanceof klcs.View) {
  // 事件代理总对象
  this._current = total.current || document;

  // 控制器函数指向，control用于内部调用
  // this._Controller = this.control = total.control;

  // 事件绑定函数
  this._eventsFunction = total.eventsFunction;

  // 触发事件绑定
  this.bindEvent(total.events);

  // 触发初始化事件
  total.initialize();
} else {
  return new klcs.View(total);
}
};

// 视图原型方法
klcs.View.prototype = {
constructor: klcs.View,
bindEvent: function (events) {
  // var _this = this;
  var callback = function (value, key) {
    // 去除两边空格，并拆分
    var eventType = "",
      fn = null,
      arr = klcs.splitSpace(klcs.trim(value));

    if (arr.length === 1) {
      eventType = "click";
      fn = arr[0];
    } else if (arr.length >= 2) {
      eventType = arr[0];
      fn = arr[1];
    }

    // 处理事件函数不存在的情况，jq给的提示不明显
    if (this._eventsFunction[fn] === undefined) {
      throw Error(fn + "函数不存在");
    }

    $(this._current).on(eventType, key, this._eventsFunction[fn]);
  };

  callback = klcs.bind(this, callback);
  klcs.each(events, callback);
},
};

// 全局路由值存放
var _route;

// 监听路由变化
klcs.route = function (callback, array) {
var flag = klcs.isFunction(callback);
var flagArr = klcs.isArray(array);

// 如果参数是函数，则进行绑定
if (flag) {
  root.onhashchange = function () {
    // hash值其实有没有#都一样，没有的话自动会补
    var hash = root.location.hash.substring(1);

    // 如果有第二个参数且为数组
    if (array !== undefined && flagArr) {
      if (array.indexOf(hash) !== -1) {
        _route = hash;

        // 防止一样的hash触发事件
        if (_route === hash) {
          return;
        }

        callback(hash);
      }
    } else {
      callback(hash);
    }
  };
}
};

// // 初始化ajax步骤
// // total{before:fn1,complete:fn2}
// klcs.ajaxInit = function(total) {

// 	// 初始化aiax步骤
// 	$.ajaxSetup({
// 		dataType: "json",
// 		beforeSend: function() {
// 			if (klcs.isFunction(total.before)) {
// 				total.before();
// 			}
// 		},
// 		type: "GET",
// 		complete: function() {
// 			if (klcs.isFunction(total.complete)) {
// 				total.complete();
// 			}
// 		},
// 		error: function(xhr) {
// 			console.warn("请求错误：errno=" + xhr.status +
// 						 ";statusText=" + xhr.statusText);
// 		}
// 	});
// };

// // 针对zepto中无ajaxSetup这个方法
// // 添加一个已写的代码包装器ajax常量
// // val = {beforeSend:fn1, complete:fn2};
// klcs.ajaxConstant = function(val) {

// 	// var setupArray = ["beforeSend", "complete"];
// 	var obj = {
// 		dataType: "json",
// 		type: "GET",
// 		error: function(xhr) {
// 			console.warn("请求错误：errno=" + xhr.status +
// 					";statusText=" + xhr.statusText);
// 		}
// 	};

// 	if (klcs.isObject(val) === false) {
// 		val = {};
// 	}
// 	// klcs.forEach(["beforeSend", "complete"], function(value, key) {
// 	// 	if (key) {

// 	// 	}
// 	// });
// 	klcs.mixin(true, obj, val);

// 	return function(add) {
// 		var temp = klcs.clone(obj);
// 		klcs.mixin(true, temp, add);
// 		// 触发
// 		$.ajax(temp);
// 	};
// };

// 出现错误的时候执行的全局操作
klcs.error = function (fn) {
window.onerror = function () {
  fn();
};
};

// 非立即执行版,防抖函数
klcs.debounce = function (func, wait) {
let timer;
return function () {
  let context = this; // 注意 this 指向
  let args = arguments; // arguments中存着e

  if (timer) clearTimeout(timer);

  timer = setTimeout(() => {
    func.apply(this, args);
  }, wait);
};
};

// 判断对象属性数量// flag为true则除去原型链上的属性
klcs.propertyNumber = function (total, flag) {
var number = 0;

klcs.forEach(total, function (value, key) {
  if (flag === true) {
    if (total.hasOwnProperty(key) === false) {
      // no code
    } else {
      number++;
    }
  } else {
    number++;
  }
});
return number;
};

// 切换数据源，类似于虚拟dom

klcs.toggle = function (object) {
var items = object.items;
var el = object.el;
var fn = object.fn;
var _arr = [];
var index = 0; // 当前下标
var flag = object.isOwnProperty || false;
var length = klcs.propertyNumber(items, flag);
var type = object.type || "click";

klcs.forEach(items, function (value, key) {
  _arr.push(key);
});
$(document).on(type, el, function (event) {
  fn(items[_arr[index++]], event);
  if (index === length) {
    index = 0;
  }
});
};

// setTimeout(fn,0)// 可以排到队列的最后面，可以防止与route的改变冲突
klcs.timeFinal = function (total, fn) {
var _setTimeout = klcs.bind(total, fn);
// 把该运行代码放到底部执行
setTimeout(_setTimeout, 0);
};

// 节流
klcs.throttle = function (callback, time) {
var fn = callback;

return function () {
  if (fn) {
    // 保持this指向与参数的正常传递
    fn.apply(this, arguments);

    // 清空回调
    fn = null;

    setTimeout(function () {
      fn = callback;
    }, time || 1000);
  }
};
};

// 绑定一个倒计时
// time为秒数
klcs.countDown = function (time, fn) {
var _time = time;
setInterval(function () {
  time--;
  var nowTime = time;
  fn(nowTime, time); // 回调
}, 1000);

return function () {
  time = _time;
};
};

// 计算日期间隔
// daysDiff(new Date('2021-05-10'), new Date('2021-11-25'))
klcs.daysDiff = (start, end) => {
// debugger
return Math.ceil(Math.abs(date - end) / 86400000);
};

// 加减日期函数
// 没传分割符号默认是“-”
klcs.timeHandle = function (inputTime, value, symbol) {
var year = "",
  month = "",
  day = "",
  totalTime = null;

symbol = symbol === undefined ? "-" : symbol;
// 格式化输入日期
inputTime = new Date(inputTime);

// 得到目标日期
totalTime = inputTime.setDate(inputTime.getDate() + (value ? value : 0));
totalTime = new Date(totalTime);

// 为月与日补0，凑成两位
year = totalTime.getFullYear(); // getYear已经不推荐使用了
month = totalTime.getMonth() + 1; // getMonth是从0月开始计数的
day = totalTime.getDate();

// 给日月补0
if (month < 10) {
  month = "0" + month;
}
if (day < 10) {
  day = "0" + day;
}

// 拼接日期，我这边只做了年月日处理
totalTime = year + symbol + month + symbol + day;

return totalTime;
};

klcs.timeHandle1 = function (inputTime, value, symbol) {
var year = "",
  month = "",
  day = "",
  hours = "",
  minte = "",
  totalTime = null;

symbol = symbol === undefined ? "-" : symbol;
// 格式化输入日期
inputTime = new Date(inputTime);

// 得到目标日期
totalTime = inputTime.setDate(inputTime.getDate() + (value ? value : 0));
totalTime = new Date(totalTime);

// 为月与日补0，凑成两位
year = totalTime.getFullYear(); // getYear已经不推荐使用了
month = totalTime.getMonth() + 1; // getMonth是从0月开始计数的
day = totalTime.getDate();
hours = totalTime.getHours();
minte = totalTime.getMinutes();
var second = totalTime.getSeconds();
// 给日月补0
if (month < 10) {
  month = "0" + month;
}
if (day < 10) {
  day = "0" + day;
}

// 拼接日期，我这边只做了年月日处理
totalTime =
  year +
  symbol +
  month +
  symbol +
  day +
  " " +
  hours +
  ":" +
  minte +
  ":" +
  second;

return totalTime;
};

// 客户端跳转OnClientCmd接口，动态引入api.js
// 在65上加载貌似是55ms还是比较快的
klcs.clientCmd = function (toClient) {
if (root.API === undefined) {
  klcs.addScript("/thsft/js/api/api.js", function () {
    root.API.OnClientCmd(toClient);
  });
  return;
} else {
  root.API.OnClientCmd(toClient);
}
};

// 简单的promise包装器
klcs.Promise = function () {
if (!(this instanceof klcs.Promise)) {
  return new klcs.Promise();
}
//订阅列表
this.subscribeList = [];
};

klcs.Promise.prototype = {
constructor: klcs.Promise,
// 订阅
subscribe: function (fn) {
  this.subscribeList.push(fn);
  return this;
},
// 等待执行的函数
when: function (fn) {
  // 加定时器可以放在队列尾部
  klcs.timeFinal(this, fn);

  return this;
},
// 触发事件执行
deliver: function () {
  // this都指向Promise实例
  var _forEach = klcs.bind(this, function (value) {
    value();
  });

  klcs.forEach(this.subscribeList, _forEach);
},
};

// 简易模板
klcs.template = function (str, value, symbol) {
// 总模板数据
var html = "";

// 内部循环渲染对象属性方法
var _each = function (value, temp) {
  // 循环对象数据
  klcs.forEach(value, function (value, key) {
    var reg = new RegExp("{{" + key + "}}", "ig");
    if (!value) {
      value = symbol || "";
    }
    temp = temp.replace(reg, value);
  });
  return temp;
};

if (klcs.isObject(value) === true) {
  html += _each(value, str);
} else if (klcs.isArray(value) === true) {
  // 循环数组
  klcs.forEach(value, function (value) {
    var temp = str;
    html += _each(value, temp);
  });
} else {
  console.info("please input array or object");
}
return html;
};

// 初始化客户端字体 klcs.initFontSize
klcs.initFontSize = function (size) {
// 判断手机型号iphone或gphone
var model = getPlatform();
var def = size || 1;

// 此命令只需要安卓起作用
if (model == "gphone") {
  callNativeHandler(
    "webViewFontController",
    // 1为默认字体，小字体设0
    '{"fontsize":"' + def + '", "switch":"0"}',
    function (data) {
      // somecode
    }
  );
}
};

// 启用underscore 启用Mustache.js类型模板
klcs.underToMustache = function () {
// 设置Mustache.js类型的模板语法
// 这句话需要写在_.template()方法之前
_.templateSettings = {
  interpolate: /\{\{=([\s\S]+?)\}\}/g,
  evaluate: /\{\{([\s\S]+?)\}\}/g,
  escape: /\{\{-([\s\S]+?)\}\}/g,
};
};

// 通过undescore模板渲染页面
// klcs.tempRender("<a>234234234<%=a%></a>", document.body,{a:"aaa"})
klcs.tempRender = function (template, total, data, type) {
template = _.template(template);

var compiled = template(data);
if (total instanceof jQuery || total instanceof Zepto) {
  total[type || "html"](compiled);
} else {
  total.innerHTML = compiled;
}
};

// 冒泡排序法对数组进行排序
// 第一个参数如果是"asc"则是正序从小到大(默认)
// 如果是"desc",则倒叙，从大到小
//arr = [85, 24, 63, 45, 17, 31, 96, 50];
klcs.sort = function () {
// 冒泡排序法排序
var temp,
  flag = false, // 是否交换过
  state = false, // 是否需要倒序
  argue = arguments[0],
  arr,
  length; // 7
// 因为排序次数只要比数组长度少一次
// 外层循环只要排长度 - 1即可，

if (typeof argue === "string") {
  // switch用的是严格等
  switch (argue) {
    case "asc":
      state = false;
      break;
    case "desc":
      state = true;
      break;
    default:
      throw "请传入正确的排序方式'asc'或'desc'";
  }

  arr = arguments[1];
} else {
  arr = argue;
}

length = arr.length - 1;

// 每一次排序都会在数组的顶部出现一个符合要求的数组成员
for (var i = 0; i < length; i++) {
  // 内层循环
  // 最上面的元素可以通过i除去排序
  // 也就是说已经排好的不需要再排了
  for (var j = 0; j < length - i; j++) {
    if (arr[j] > arr[j + 1]) {
      temp = arr[j + 1];
      arr[j + 1] = arr[j];
      arr[j] = temp;

      // 如果该论循环已经发生了交换
      flag = true;
    }
  }

  // 如果当前排序已经没有变化，则直接退出循环
  if (flag === false) {
    break;
  }
}

if (state === true) {
  arr = arr.reverse();
}

return arr;
};

// 使用二分法查找数组中的指定的项
klcs.binarySearch = function (arr, total) {
var index = 0;

function _wrap(arr, total) {
  var length = arr.length;
  var midIndex = Math.floor(length / 2);

  if (length <= 1) {
    if (arr[0] == total) {
      return {
        index: index,
        total: total,
      };
    }
    return "no this item";
  } else {
    if (arr[midIndex] > total) {
      return arguments.callee(arr.slice(0, midIndex), total);
    } else if (arr[midIndex] < total) {
      index += midIndex;
      return arguments.callee(arr.slice(midIndex), total);
    } else {
      index += midIndex;
      return {
        index: index,
        total: total,
      };
    }
  }
}

return _wrap(arr, total);
};

// 内部对挂载的方法按名称进行排序
klcs.method = function (fn) {
var names = [];
klcs.forEach(fn, function (fn, name) {
  if (klcs.isFunction(fn)) {
    names.push(name);
  }
});

return names.sort();
};

// 调用客户端下载框 klcs.clientDown("abc", "/thsft/Istrategy/abc.pdf", ".xls");
// type可选，如果url能够取到地址串，则不会被type覆盖，
// 如果地址串后面的url没有解析出类型，则会被type覆盖
klcs.clientDown = function (name, url, type) {
// replace不适合截取
var typeArray = url.match(/\.(\w{2,4})?$/g);

type = typeArray ? typeArray[0] : null || type;

// 注意这里一定要有type,不然导致整个页面链接改变
var href =
  "ifind://!command=down&valuectrl=1&filename=" +
  name +
  type +
  "&url=http://" +
  host +
  url;

root.location.href = href;
};

// 序列化jq生成的表单数组
klcs.serizeArrForm = function (arr) {
var total = {};

klcs.forEach(arr, function (value, key) {
  total[arr[key].name] = arr[key].value;
});
return total;
};
  
klcs.dialogDrag = function () {

  const dialogHeaderEl = this.$refs.myDialog.$el.firstChild.firstChild; //  el-dialog header
  const dragDom = this.$refs.myDialog.$el.firstChild;   // el-dialog is-fullscreen
  // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
  const getStyle = (function () {
    if (window.document.currentStyle) {
      return (dom, attr) => dom.currentStyle[attr];
    } else {
      return (dom, attr) => getComputedStyle(dom, false)[attr];
    }
  })();

  dialogHeaderEl.onmousedown = (e) => {
    // 鼠标按下，计算当前元素距离可视区的距离
    const disX = e.clientX - dialogHeaderEl.offsetLeft;
    const disY = e.clientY - dialogHeaderEl.offsetTop;

    const dragDomWidth = dragDom.offsetWidth;
    const dragDomHeight = dragDom.offsetHeight;

    const screenWidth = document.body.clientWidth;
    const screenHeight = document.body.clientHeight - 1;

    const minDragDomLeft = dragDom.offsetLeft;
    const maxDragDomLeft =
      screenWidth - dragDom.offsetLeft - dragDomWidth;

    const minDragDomTop = dragDom.offsetTop;
    const maxDragDomTop =
      screenHeight - dragDom.offsetTop - dragDomHeight;

    // 获取到的值带px 正则匹配替换
    let styL = getStyle(dragDom, "left");
    let styT = getStyle(dragDom, "top");

    if (styL.includes("%")) {
      styL =
        +document.body.clientWidth * (+styL.replace(/\%/g, "") / 100);
      styT =
        +document.body.clientHeight * (+styT.replace(/\%/g, "") / 100);
    } else {
      styL = +styL.replace(/\px/g, "");
      styT = +styT.replace(/\px/g, "");
    }

    document.onmousemove = function (e) {
      // 通过事件委托，计算移动的距离
      let left = e.clientX - disX;
      let top = e.clientY - disY;

      // 边界处理
      if (-left > minDragDomLeft) {
        left = -minDragDomLeft;
      } else if (left > maxDragDomLeft) {
        left = maxDragDomLeft;
      }

      if (-top > minDragDomTop) {
        top = -minDragDomTop;
      } else if (top > maxDragDomTop) {
        top = maxDragDomTop;
      }
      // 移动当前元素
      dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT
        }px;`;
    };
    document.onmouseup = (e) => {
      document.onmousemove = null;
      document.onmouseup = null;
    };
  };
}

// 执行一次的函数包装器
klcs.once = function (fn) {
var totalFn = fn;
if (klcs.isFunction(totalFn) === false) {
  throw "请传入函数方法";
}
return function () {
  if (klcs.isFunction(totalFn)) {
    totalFn();
    totalFn = null;
  }
};
};

  klcs.pluck = function (array, key) {
    return array.map(function (obj) {
      return obj[key];
    });
  }



// 数组拆分函数
klcs.arrayGroup = function (arr, number) {
var oldArray = arr;
var newArray = [];

var level = arr.length / number;
for (var i = 0; i < level; i++) {
  newArray.push(oldArray.slice(i * number, (i + 1) * number));
}

return newArray;
};

// 将对象转成请求参数字符串,格式化参数 klcs.objToSearch({a:1,b:2}) => a=1&b=2
klcs.objToSearch = klcs.formatUrlParams = function (obj) {
var str = "?";
klcs.forEach(obj, function (value, key) {
  str += key + "=" + value + "&";
});

return str.slice(0, -1);
};

/**
 * 判断平台是PC还是移动端
 * pc
 * mobile
 * @这个准备停止维护
 * @后续使用klcs.platformType函数
 */
klcs.platformType = function () {
var type;
//获取语言

//平台、设备和操作系统
// var system = [
//     {name :"pc",  verify:false},
//     {name :"mobild",  verify:false},
// ];

// var p = navigator.userAgent;

//     // navigator.appVersion
// 	var pc =
//         	(
// 				//
// 				navigator.userAgent.indexOf("Win") !== -1 ||
// 				// "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1_0) AppleWebklcs/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36"
// 				navigator.userAgent.indexOf("Mac") !== -1 ||
// 				navigator.userAgent.indexOf("X11") !== -1
// 				// navigator.userAgent.indexOf("Linux") !== -1
// 			);

var ua = navigator.userAgent.toLowerCase();
if (/mobi/i.test(ua)) {
  // 手机浏览器
  type = "mobile";
} else {
  // 非手机浏览器
  type = "pc";
}

// var output = klcs._.find(system, function(num) {
// 	return num.pc = true;
// })

// if (pc) {
// 	type="pc"
// } else {
// 	type="mobile"
// }

return type;
};

//     klcs.fpiExport([
// {
// 	name: "路人甲",
// 	phone: "123456789",
// 	email: "000@123456.com"
// },
// {
// 	name: "炮灰乙",
// 	phone: "123456789",
// 	email: "000@123456.com"
// },
// {
// 	name: "土匪丙",
// 	phone: "123456789",
// 	email: "000@123456.com"
// },
// {
// 	name: "流氓丁",
// 	phone: "123456789",
// 	email: "000@123456.com"
// }
// ],"执法记录管理",[{label:"名字",key:"name"},{label:"邮箱",key:"email"}]);

/**
  表格导出excel
  @arr 要导出的json数据
  @tHeader 表头数组
  @fileName 文件名
  */
klcs.fpiExport = function (arr, fileName, data) {
if (!arr) {
  console.warn("请输入数据源arr");
  return;
}

// 每行数据源value
arr = arr.map((value) => {
  var obj = {};
  klcs._.forEach(data, (val, key) => {
    obj[val.prop] = value[val.prop] || "";
  });
  return obj;
});
// console.log(arr)

//列标题，逗号隔开，每一个逗号就是隔开一个单元格 \n必须要有不然在同一行
var pluck = klcs._.pluck(data, "label");
let str = pluck.join(",") + "\n";

//增加\t为了不让表格显示科学计数法或者其他格式
for (let i = 0; i < arr.length; i++) {
  for (let item in arr[i]) {
    // console.log(arr[i][item])
    // 去除敏感的逗号,换行用
    arr[i][item] = (arr[i][item] + "").replace(/,/g, "");
    str += `${arr[i][item] + "\t"},`;
  }
  str += "\n";
}

console.log(str);
//encodeURIComponent解决中文乱码
let uri = "data:text/csv;charset=utf-8,\ufeff" + encodeURIComponent(str);

//通过创建a标签实现
var link = document.createElement("a");
link.href = uri;

//对下载的文件命名
link.download = fileName + new Date().toLocaleDateString() + "表.xlsx";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};

// 获取秒级时间戳
klcs.getSecondParse = function (date) {
return Date.parse(date || new Date()) / 1000;
};

/**
 * 百分化
 * @total :转换目标
 * @num : 转换后的位数
 * @signFlag : 百分号标志
 */
klcs.percent = function (total, num, signFlag) {
total = total * 100;
if (Number.isNaN(total)) {
  total = 0;
}
return signFlag
  ? Number(total.toFixed(num)) + "%"
  : Number(total.toFixed(num));
};

/**
 * @description: sum数组求和主要注意的是遇到是字符串的数字,以及不是数字的--等,统一计算为0
 * @author: kelichao
 * @update:
 * @length :保留的位数长度.不然的话会出现精度问题
 */
klcs.sum = function (arr, length) {
var sum = _.reduce(
  arr,
  function (memo, num) {
    if (typeof num === "string") {
      num = Number(num);
      if (isNaN(num)) {
        num = 0;
      }
    }
    return memo + num;
  },
  0
);
return Number(sum.toFixed(length || 0));
};

klcs.first = function (arr) {
return arr[0] || {};
};

/**
 * @description: 求平均数
 * @author: kelichao
 * @arr : 数组
 * @length : 截取位数
 * @flag : 是否要将空值"--",这类数值计算入总数
 * @demo : klcs.average([1,2,3,"--"], 2, true)
 */
klcs.average = function (arr, length, flag) {
var leng = 0;
var sum = _.reduce(
  arr,
  function (memo, num) {
    // 防止 "123" 这种情况
    if (["number", "string"].includes(typeof num)) {
      num = Number(num);
      flag && leng++;
      if (isNaN(num)) {
        num = 0;
      } else {
        !flag && leng++;
      }
    }
    return memo + num;
  },
  0
);
console.log(arr, leng);
return Number((sum / (leng || 1)).toFixed(length || 0));
};



klcs.deleteLastStr = function (str) {
var basic = str;

basic = basic.substring(0, basic.length - 1);

return basic;
};

klcs.devEvement = function () {
return location.hostname == "127.0.0.1" || location.hostname == "localhost";
};

klcs.createFormObj = function (element) {
var obj = {};

_.each(element.serializeArray(), function (value, key) {
  obj[value.name] = value.value;
});

return obj;
};

klcs.getFileName = function (name) {
var fileName = name;
var lastIndex = fileName.lastIndexOf(".");
var type = fileName.substring(lastIndex + 1); // png
return type;
};

function downloadFile(url) {
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "blob";
xhr.send();
xhr.onload = function () {
  const res = xhr.response;
  const fileUrl = URL.createObjectURL(res);
  const eleA = document.createElement("a");
  eleA.setAttribute("href", fileUrl);
  eleA.setAttribute("download", "");
  eleA.click();
  URL.revokeObjectURL(fileUrl);
  // window.open(fileUrl) // 同样可新标签中打开进行预览
  // img.src=fileUrl  // 设置为img标签src
};
}

klcs.addOld = function (str) {
// var str = "companyId";
var newStr = "old" + str.slice(0, 1).toUpperCase() + str.slice(1);
console.log(newStr); // 输出oldCompanyId
};

// let arr = [{deptId:1}, {deptId:2, parentId:1}, {deptId:3, parentId:2}];
klcs.createObj = function (arr) {
// 定义一个 map 对象，用于存储每个 deptId 对应的对象
let map = {};
for (let i = 0; i < arr.length; i++) {
  map[arr[i].deptId] = arr[i];
}

// 遍历数组，将 parentId 相同的项添加到 parent 的 child 属性中
for (let i = 0; i < arr.length; i++) {
  let item = arr[i];
  if (item.parentId) {
    let parent = map[item.parentId];

    if (parent) {
      if (!parent.child) {
        parent.child = [];
      }
      parent.child.push(item);
      arr.splice(i, 1); // 删除已经成为 child 的对象
      i--; // 遍历的索引需要减 1
    }
  }
}

// 删除 parentId 属性，并递归添加子节点
function deleteAndAddChild(obj) {
  if (obj.parentId) {
    delete obj.parentId;
    if (obj.child) {
      for (let i = 0; i < obj.child.length; i++) {
        deleteAndAddChild(obj.child[i]);
      }
    }
  }
}

for (let i = 0; i < arr.length; i++) {
  deleteAndAddChild(arr[i]);
}
return arr;
};

klcs.getContentHeight = function (id, callback) {
const query = wx.createSelectorQuery();
const that = this;
var obj;
//选择id
query
  .select(id)
  .boundingClientRect(function (rect) {
    console.log(rect); // 这里包含内容 的宽高
    obj = rect;
    callback(obj);
  })
  .exec();
};

klcs.timeJuLi = function () {
if (!this.value.workDate) {
  return 0;
}

var date1 = new Date();
var date2 = new Date(this.value.workDate);
var timeDiff = Math.abs(date2.getTime() - date1.getTime());
var yearDiff = timeDiff / (1000 * 3600 * 24 * 365);
var months = yearDiff * 12; // 将1.6转换为16个月
var years = Math.floor(months / 12); // 取整得到年份
var remainingMonths = Math.floor(months % 12); // 取余得到剩余月份

var total = years.toFixed(0) + "年" + remainingMonths.toFixed(0) + "个月";
// console.log(years, total); // 输出：1年4个月

this.value.workAge = total;
return total;
};

klcs.success = function (message) {
message.success("成功！" || message);
};
klcs.error = function (message) {
message.error("出错！" || message);
};
klcs.warning = function (message) {
message.warning("警告！" || message);
};

/************************************************************************************************

********                  分割线   后面为继承  方法添加写在上方        ********************************

************************************************************************************************/

// 为了能使用OOP形式的调用，将klcs的所有方法挂载到原型
// 去除不是function类型的。
// 用于添加自定义方法，此方法放到最后执行
/*
  // 用法
  klcs.extend({
    "kelichao":function() {
      console.log("name");
    }
  })
*/
// klcs.extend( [deep ], target, object1 [, objectN ] )...
klcs.extend = klcs.fn.extend = function () {
var length = arguments.length,
  i = 1,
  target = arguments[0] || {},
  // 参考了underscore
  // 得到排序后的所有方法名数组
  sortFuncName = klcs.method(target);

// 如果第一个参数是布尔状态,就把对象切到第二个参数
if (typeof target === "boolean") {
  target = arguments[1] || {};
  i++;
}

// 如果目标对象不是对象或者函数，则返回空对象
if (!(klcs.isObject(target) === true || klcs.isFunction(target) === true)) {
  target = {};
}

if (length === 1) {
  klcs.forEach(sortFuncName, function (value) {
    // 1.挂载到对象名下
    // 2.挂载到原型链上
    // 3.先保存下当前函数地址(后面用于wrapped)
    var func = (klcs[value] = target[value]);

    // 如果这个方法是之前绑定在原型上的，
    // 那么不做覆盖处理,例如klcs.fn.extend
    if (klcs.isUndefined(fn[value]) === false) {
      return;
    }

    // OOP调用,对方法内部传入参数进行修改
    // 否则一般写法为 fn[value] = func;
    fn[value] = function () {
      // 第一个参数
      // 由于这里调用点是klcs的实例对象
      // this是当前的klcs实例对象
      var args = [this._wrapped];

      // arguments 为 name 方法需要的其他参数
      // 用arguments拿参数的好处是不需要制定形参名
      // 通过apply调用可以一次性传入多个数组
      // 不管有几个参数，我都可以拿到。
      // 执行后args组成一个新数组，包含agruments
      // 把第一个参数改为实例对象
      // 这个push方式相比传统的push，可以一次性传入多个数组值
      push.apply(args, arguments);

      // 将this指向klcs ,传入改造后的参数组
      return func.apply(klcs, args);
    };
  });
} else {
  // 暂无处理
}
};

klcs.extend(klcs);

window.klcs = klcs;
}.call(this));

export default klcs;
// klcs.Module 的getter,setter方法

// 滑动到底部触发的事件，滑动到某个元素的高度触发事件
